/*
 * Copyright (C) 2008,2009,2010,2011  Paulo Cesar Pereira de Andrade.
 *
 * This is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * Authors:
 *	Paulo Cesar Pereira de Andrade
 */

#ifndef _eobject_h
#define _eobject_h

#include "etypes.h"

#define ecar(list)		(((econs_t *)(list))->car)
#define ecdr(list)		(((econs_t *)(list))->cdr)

#define ecaar(list)		ecar(ecar(list))
#define ecadr(list)		ecar(ecdr(list))
#define ecdar(list)		ecdr(ecar(list))
#define ecddr(list)		ecdr(ecdr(list))
#define ecaaar(list)		ecar(ecaar(list))
#define ecaadr(list)		ecar(ecadr(list))
#define ecadar(list)		ecar(ecdar(list))
#define ecaddr(list)		ecar(ecddr(list))
#define ecdaar(list)		ecdr(ecaar(list))
#define ecdadr(list)		ecdr(ecadr(list))
#define ecddar(list)		ecdr(ecdar(list))
#define ecdddr(list)		ecdr(ecddr(list))

#define econs(car, cdr)		enew_cons(car, cdr)
#define econs_nil(car)		enew_cons_nil(car)

#define einteger(pointer, value)					\
    enew_integer_novm(pointer, value)

#define efloat(pointer, value)						\
    enew_float(pointer, value)

#define ecdd(pointer, dd)	enew_cdd(pointer, dd)
#define ecqq(pointer, qq)	enew_cqq(pointer, qq)

#define evector(pointer, type, length, rank)				\
    enew_vector_novm(pointer, type, length, rank)

#define evector_type_p(type)	(((type) & (t_vector|t_pointer)) == t_vector)

#define etype(object)		eobject_type_novm(object)

#define efixnum(integer)	enew_fixnum(integer)

#define erplaca(list, object)	ecar(list) = (object)
#define erplacd(list, object)	ecdr(list) = (object)

#define econs_p(object)		(etype(object) == t_cons)

#define v_enter()		eint32_t	_ssi = thread_self->gsi
#define v_check(count)							\
    do {								\
	if (unlikely(thread_self->gsi + count > thread_self->gsl)) {	\
	    ++gc_disable;						\
	    thread_self->gsl += 16;					\
	     eresize_object((eobject_t *)&thread_self->egs,		\
			    thread_self->gsl * sizeof(eobject_t));	\
	    --gc_disable;						\
	}								\
    } while (0)
#define v_push(object)		thread_self->egs[thread_self->gsi++] = object
#define v_pop()			thread_self->egs[--thread_self->gsi]
#define v_top()			thread_self->egs[thread_self->gsi - 1]
#define v_dec()			--thread_self->gsi
#define v_leave()		thread_self->gsi = _ssi

#define fixnum_mask		1

#define fixnum_bits		1

#define efixnum_p(pointer)	(((eword_t)pointer) & fixnum_mask)

#define enew_fixnum(value)						\
    ((eobject_t)((((eword_t)(value)) << fixnum_bits) | fixnum_mask))

#define eget_fixnum(pointer)						\
    ((eword_t)(pointer) >> fixnum_bits)

#define most_positive_fix32						\
    ((1l << (sizeof(eint32_t) * 8 - (fixnum_bits + 1))) - 1)

#define most_positive_fixnum						\
    ((1l << (sizeof(eobject_t) * 8 - (fixnum_bits + 1))) - 1)

#define most_negative_fixnum						\
    (-1l << (sizeof(eobject_t) * 8 - (fixnum_bits + 1)))

#define eget_integer(integer)						\
    (efixnum_p(integer) ? eget_fixnum(integer) : (*(eint_t *)integer))

#define erecord_type_p(t)	(t > elast_basic_type && t < ets_idx)
#define elast_basic_type	t_float32

/*
 * Types
 */
enum etype {
    t_void,
    t_int,
    t_float,

    t_mpz,
    t_shz,
    t_mpq,
    t_shq,
    t_mpr,
    t_shr,
    t_cdd,
    t_cqq,
    t_sqq,
    t_mpc,
    t_shc,
    t_rgz,
    t_rgq,
    t_rgr,
    t_rqq,
    t_rgc,

    t_int8,
    t_uint8,
    t_int16,
    t_uint16,
    t_int32,
    t_uint32,
#define t_int64			t_int
    t_uint64,
#define t_uint			t_uint64
    t_float32,
#define t_float64		t_float

    /* internally defined types */
    t_bitmap,			/* drawable object */
    t_pixmap,			/* drawable object */
    t_window,			/* window object */
    t_event,			/* window event */
    t_context,			/* graphics context */
    t_timer,			/* milisecond resolution user interface timer */
    t_point,
    t_rect,

    /* vm special types */
    t_stream	= 0x01000000,
    t_thread,
    t_mutex,
    t_hashtable,
    t_hashentry,
    t_switch,
    t_globalref,
    t_localref,
    t_t_localref,
    t_stackref,
    t_t_stackref,
    t_anonref,
    t_vectorref,
    t_recordref,
    t_hashref,

    /* parser types */
    t_cons	= 0x02000000,
    t_hash,
    t_entry,
    t_symbol,
    t_macro,

    /* order is t_basic, t_struct, t_class, t_union */
    t_basic,
    t_struct,
    t_class,
    t_union,

    t_function,
    t_method,
    t_block,
    t_tag,
    t_label,

    /* code list */
    t_code_label,
    t_code_simple,
    t_code_immediate,
    t_code_stack,
    t_code_symbol,
    t_code_hash,
    t_code_vector,
    t_code_record,
    t_code_class,
    t_code_enter,
    t_code_apply,
    t_code_return,
    t_code_logic,
    t_code_except,
    t_code_note,

    t_ast_list,

    t_ssa_store,
    t_ssa_load,

    t_dfa_rule,

#if JITVM
    t_jit_node,
    t_jit_block,
    t_jit_state,
    t_jit_function,
#endif

    /* dfa types */
    t_exact,
    t_inexact,
    t_dynamic		= 0x80000000,

    /* type modifiers */
    t_lambda		= 0x04000000,
    t_resize		= 0x08000000,
    t_pointer		= 0x10000000,
    t_vector		= 0x20000000,
    t_string		= t_vector|t_uint8,
    t_vector_int8	= t_vector|t_int8,
    t_vector_uint8	= t_vector|t_uint8,
    t_vector_int16	= t_vector|t_int16,
    t_vector_uint16	= t_vector|t_uint16,
    t_vector_int32	= t_vector|t_int32,
    t_vector_uint32	= t_vector|t_uint32,
    t_vector_int64	= t_vector|t_int64,
    t_vector_uint64	= t_vector|t_uint64,
    t_vector_float32	= t_vector|t_float32,
    t_vector_float64	= t_vector|t_float64,
    t_const		= 0x40000000,
    t_const_string	= t_const|t_string,

    /* top bit is reserved/unused in the gc code */
    t_fixnum		= 0x80000000,
};

struct econs {
    eobject_t	cdr;
    eobject_t	car;
};

typedef struct edim {
    eint32_t	dim;
    eint32_t	mul;
} edim_t;

struct evector {
    union {
	eint8_t		*i8;
	euint8_t	*u8;
	eint16_t	*i16;
	euint16_t	*u16;
	eint32_t	*i32;
	euint32_t	*u32;
	eint64_t	*i64;
	euint64_t	*u64;
	efloat32_t	*f32;
	efloat64_t	*f64;
	eobject_t	*ptr;
	eobject_t	 obj;
    } v;
    eint32_t	length;
    eint32_t	offset;
    /* TODO (or just remove this comment later)
     * add common lisp like array features here, including:
     * o fill pointer
     * o displacement
     * NOTE ptr must be the first field
     */
    eint32_t	rank;
    edim_t	dims[1];
};

struct epointer {
    /*	  Note that are two type information available here:
     *	1. type of the object itself.
     *	2. type of the ptr field.
     *
     *	  For vector pointers, the main type should be:
     *		<type>|t_vector|t_pointer
     *	and the ptr field type should be:
     *		<type>|t_vector
     *	where <type> is identical for both types, or, the
     *	main type is t_void.
     *
     *	  For record pointers, the main type should be:
     *		<type>|t_pointer
     *	where <type> matches the record field type,
     *	and the ptr field type should be:
     *		<type>
     *	where <type> is a valid record unique type identifier.
     *
     *	  It should be noted that record pointers should never
     *	become dangling pointers because 'ptr' should never
     *	change type, neither be moved.
     *	  Even if it is known that there is only one reference
     *	for the object, it should not change type/address,
     *	otherwise the pointer logic needs update.
     *
     *	  FIXME unlike integers and floats, it probably should
     *	be made possible to share some other numeric types,
     *	and do a 'copy on write' like logic. This should still
     *	be ok with the current approach for pointers, as there
     *	should not exist pointer objects that treats things like
     *	bignums as vectors or structures.
     */
    eobject_t	ptr;
    eint32_t	length;
    eint32_t	offset;
};

struct elambda {
    union {
	eobject_t	lambda;
	void (*builtin)(eint32_t);
    } code;
    eint32_t		args;
    eint32_t		bits;
};

struct evalue {
    eint32_t		  t;
    eint32_t		  align;
    union {
	/* packed types */
	eint8_t		  i8;
	euint8_t	  u8;
	eint16_t	  i16;
	euint16_t	  u16;
	eint32_t	  i32;
	euint32_t	  u32;
	eint64_t	  i64;
	euint64_t	  u64;
	efloat32_t	  f32;
	efloat64_t	  f64;

	/* dynamic types */
	eint_t		  i;
	eint_t		  u;
	efloat_t	  d;
	eobject_t	  o;
	empz_t		  mpz;
	empq_t		  mpq;
	empr_t		  mpr;
	ecdd_t		  cdd;
	ecqq_t		  cqq;
	empc_t		  mpc;

	/* cast types */
	eint8_t 	 *r;
	elambda_t	 *y;
	eobject_t	**v;
	evector_t	 *V;
	eint8_t		**ppi8;
	euint8_t	**ppu8;
	eint16_t	**ppi16;
	euint16_t	**ppu16;
	eint32_t	**ppi32;
	euint32_t	**ppu32;
	eint64_t	**ppi64;
	euint64_t	**ppu64;
	efloat32_t	**ppf32;
	efloat64_t	**ppf64;
	evalue_t	 *ref;
	elocalref_t	 *loc;

	/* special types */
	struct {
	    eobject_t	  vector;
	    eint32_t	  offset;
	} vref;
	struct {
	    eobject_t	  record;
	    eint32_t	  offset;
	    eint32_t	  type;
	} rref;
	struct {
	    ehashtable_t *table;
	    ehashentry_t *entry;
	} href;
    } v;
};

struct elocalref {
    ethread_t	*thread;
    eint32_t	 offset;
};

/* class data field information */
struct efield {
    eint32_t	offset;		/* data field offset */
    eint32_t	parent;		/* type of class that defined this data field */
};

/* class method information */
struct emethod {
    eobject_t	code;		/* method code */
    eint32_t	type;		/* method class */
    /* note that type is the type of the first class that defined
     * the method, and is the "signature" of the virtual method.
     * if a class sets this value, it is only available to itself
     * and its subclassess */
};

/* run time type information */
struct ertti {
    eint32_t	 size;		/* record size */
    eint32_t	 super;		/* record superclass */
    eint32_t	 nfield;	/* number of data fields */
    eint32_t	 noffset;	/* number of gc offsets */
    eint32_t	 nmethod;	/* number of class methods */
    efield_t	*fields;	/* data fields */
    eint32_t	*offsets;	/* gc offsets */
    emethod_t	*methods;	/* class methods */
    eint32_t	*translate;	/* vector of this.size eint32_t */
    /* translate is used to 'translate' a field offset into the offset
     * of the proper efield_t entry */
};

/*
 * Prototypes
 */
extern void
eadd_root(eobject_t *object);

extern void
erem_root(eobject_t *object);

#if HAVE_THREAD
extern void
enew_thread(eobject_t *pointer, eint32_t length);
#endif

extern void
enew_object(eobject_t *pointer, etype_t type, eint32_t length);

extern void
eresize_object(eobject_t *pointer, eint32_t length);

extern eint32_t
eobject_length(eobject_t object);

extern etype_t
eobject_type_novm(eobject_t object);

extern etype_t
eobject_type(eobject_t object);

extern eobject_t
enew_cons(eobject_t car, eobject_t cdr);

extern eobject_t
enew_cons_nil(eobject_t car);

extern void
enew_vector_novm(evector_t **pointer, etype_t type,
		 eint32_t length, eint32_t rank);

extern void
enew_vector(evector_t **pointer, etype_t type, eint32_t length, eint32_t rank);

extern void
erenew_vector(evector_t *vector, eint32_t length);

extern void
enew_pointer(eobject_t *pointer, etype_t type, eobject_t value, eint32_t offset);

extern void
enew_globalref(eobject_t *pointer, eobject_t object);

extern void
enew_localref(eobject_t *pointer, eint32_t offset);

extern void
enew_t_localref(eobject_t *pointer, eint32_t offset);

extern void
enew_anonref(eobject_t *pointer, eobject_t object);

extern void
enew_integer_novm(eobject_t *pointer, eint_t value);

extern void
enew_integer(eobject_t *pointer, eint_t value);

extern void
enew_float(eobject_t *pointer, efloat_t value);

extern void
enew_mpz(eobject_t *pointer, empz_t z);

extern void
enew_mpz_i(eobject_t *pointer, eint_t i);

extern void
enew_mpz_u(eobject_t *pointer, euint_t i);

extern void
enew_mpq(eobject_t *pointer, empq_t q);

extern void
enew_mpr(eobject_t *pointer, empr_t fr);

extern void
enew_cdd(eobject_t *pointer, complex double dd);

extern void
enew_cqq(eobject_t *pointer, ecqq_t qq);

extern void
enew_mpc(eobject_t *pointer, empc_t cc);

extern void
enew_lambda(eobject_t *pointer, eobject_t code, eint32_t args, eint32_t bits);

extern eobject_t
ecopy_list(eobject_t list);

extern eobject_t
ecopy_tree(eobject_t list);

extern eobject_t
elast(eobject_t list);

extern eint32_t
elength(eobject_t list);

extern eobject_t
ereverse(eobject_t list);

#if DEBUG
extern void
egc_check_root(void);

extern void
egc(void);
#endif

extern void
init_object(void);

/*
 * Externs
 */
extern ethread_t	*tmain;			/* stack */

extern void		(**ebs)(eint32_t);	/* builtin */
extern eint32_t		 ebs_idx, ebs_len;

extern eobject_t	*ecs;			/* code */
extern eint32_t		 ecs_idx, ecs_len;

extern eobject_t	*eds;			/* data */
extern eint32_t		 eds_idx, eds_len;

extern ertti_t		*ets;			/* type */
extern eint32_t		 ets_idx, ets_len;

extern erecord_t	**record_vector;	/* parse time only */

extern eobject_t	*els;			/* literal/constant */
extern eint32_t		 els_idx, els_len;

#if GC_DEBUG
extern eint32_t		 gc_debug;
#endif

extern eint32_t		 gc_disable;

#if HAVE_THREAD
extern pthread_mutex_t	 ethread_mutex;
#endif

#if DEBUG
extern int vm_running;
#endif

#endif /* _eobject_h */
